package models.automation;

import models.automation.events.ImageFixerProgress;
import models.entities.BrandEntity;
import models.entities.PictureEntity;
import models.entities.ProductEntity;
import models.exceptions.MD5ReadException;
import models.helpers.ImageHelper;
import models.repository.ImageRepositorySql;
import models.repository.common.ImageRepository;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Observable;

public class ImageFixer extends Observable {
    private ImageRepository repository = new ImageRepositorySql();

    public void notifyObservers(Object event) {
        setChanged();
        super.notifyObservers(event);
    }

    public void clearMD5() throws IOException {
        repository.clearMD5();
    }

    public void fixImages(String dataPath) throws IOException {
        int duplicatesFound = 0;
        int md5Set = 0;
        int totalImagesWithoutMD5Count = repository.getImagesWithoutMD5TotalCount();
        List<PictureEntity> images = repository.getImagesWithoutMD5();
        while (images.size() > 0) {
            for (PictureEntity pictureEntity : images) {
                try {
                    pictureEntity.md5 = getMD5(pictureEntity, dataPath);
                    PictureEntity existentImage = repository.getImageByMD5(pictureEntity.md5);
                    if (existentImage != null) {
                        duplicatesFound++;
                        fixDuplicate(existentImage, pictureEntity, dataPath);
                        md5Set++;
                    } else {
                        repository.updateMD5(pictureEntity);
                        renamePicture(pictureEntity, dataPath);
                        md5Set++;
                    }
                    notifyObservers(new ImageFixerProgress(duplicatesFound, md5Set, totalImagesWithoutMD5Count));
                } catch (MD5ReadException ex) {
                    repository.deletePicture(pictureEntity);
                    md5Set++;
                }
            }
            images = repository.getImagesWithoutMD5();
        }
    }

    private void fixDuplicate(PictureEntity existentImage, PictureEntity pictureEntity, String dataPath) throws IOException {
        List<ProductEntity> productWithDuplicatePicture = repository.getProductsByImageId(pictureEntity.id);
        List<BrandEntity> brandsWithDuplicatePicture = repository.getBrandsByImage(pictureEntity.id);
        for (BrandEntity brand : brandsWithDuplicatePicture) {
            repository.setOriginalPictureForBrand(pictureEntity.id, existentImage.id, brand.id);
        }
        for (ProductEntity product : productWithDuplicatePicture) {
            PictureEntity picture = repository.getProductImage(existentImage.id, product.id);
            if (picture == null) {
                repository.setOriginalPictureForProduct(pictureEntity.id, existentImage.id, product.id);
            } else {
                repository.deleteProductImage(pictureEntity.id, product.id);
            }
        }
        deleteDuplicate(pictureEntity, dataPath);
    }

    private void deleteDuplicate(PictureEntity duplicate, String dataPath) throws IOException {
        File duplicateImage = new File(String.format("%1$s%2$s", dataPath, ImageHelper.getOSIndependentFilePath(duplicate.filePath)));
        if (duplicateImage.exists()) {
            duplicateImage.delete();
        }
        repository.deletePicture(duplicate);
    }

    private void renamePicture(PictureEntity pictureEntity, String dataPath) throws IOException {
        String fileUrl = String.format("%1$s%2$s", dataPath, ImageHelper.getOSIndependentFilePath(pictureEntity.filePath));
        File imageFile = new File(fileUrl);
        if (imageFile.exists()) {
            String relativePath = "productimages";
            if (pictureEntity.filePath.contains("brands"))
                relativePath = "brands";
            String newFilePath = String.format("%1$s%2$s.jpg", ImageHelper.getOSIndependentFilePath("/" + relativePath + "/"), pictureEntity.md5);
            pictureEntity.filePath = newFilePath;
            File newFile = new File(String.format("%1$s%2$s", dataPath, newFilePath));
            Boolean success = imageFile.renameTo(newFile);
            if (success) {
                repository.changePath(pictureEntity);
            }
        }
    }

    private void addToDuplicatesCache(Integer originalPictureId, PictureEntity duplicatePicture, Hashtable<Integer, List<PictureEntity>> duplicates) {
        if (!duplicates.contains(originalPictureId)) {
            List<PictureEntity> images = new ArrayList<PictureEntity>();
            images.add(duplicatePicture);
            duplicates.put(originalPictureId, images);
        } else {
            duplicates.get(originalPictureId).add(duplicatePicture);
        }
    }

    private String getMD5(PictureEntity pictureEntity, String dataPath) throws IOException, MD5ReadException {
        String fileUrl = String.format("%1$s%2$s", dataPath, ImageHelper.getOSIndependentFilePath(pictureEntity.filePath));
        return getMd5(fileUrl);
    }

    public String getMd5(String fileUrl) throws IOException, MD5ReadException {
        File imageFile = new File(fileUrl);
        if (imageFile.exists()) {
            BufferedImage imageFromDBPath = ImageIO.read(imageFile);
            return ImageHelper.getMD5HashCode(imageFromDBPath);
        } else {
            throw new MD5ReadException();
        }
    }
}
